from psychopy import core, event, visual
from Eye_cup import Eye_cup
from RF import RF
import scipy

def show_moving_patch( moving_patch_info, win_size, shade=1.0, window=None, 
                       eye_cup=None, rf=None, save_frames_factor=None,
                       preview=True ):
    '''
    This is the underlying code which moves a patch_stim around on the screen.
    Inputs:
        moving_patch_info:  A list of dictionaries each dict has...
            patch_info  : This is a dictionary with entries tex, size, and units that
                              are named arguements to build the patch
            positions   : A list of x,y position pairs who's length determines the number
                              of frames that will be drawn.
            orientations: A list of same length as positions specifying the orientation
                              of the patch at every frame to be drawn.
        win_size    : The size of the window to draw to.
        shade       : 1.0 for black(fg) and white(bg)
        --- kwargs --
        window      : a psychopy.visual Window object, if not passed it will be constructed.
                    :    if passed it will not be closed (a blank screen will be drawn)
        eye_cup     : An Eye_cup object so we can draw the eye_cup on the screen
        rf          : Used to set up the new coordinate system
        save_frames_factor  : if None, then no frames are saved, otherwise it is a 
                                scaling factor so we can scale down the frames and
                                output a frames.gif (an animated gif file).
        preview             : if True then the frames will display immediately,
                                otherwise we will wait for a pulse on the parallel
                                port.
    Returns:
        times                : A list of the same length as positions which has
                                the times of the frames were displayed starting with
                                0.0 for the first frame.
    '''
    # how many frames we'll display
    num_frames = len(moving_patch_info[0]['positions'])


    # -----------------------------------------
    # -- Set up the window to display things --
    # -----------------------------------------
    if eye_cup == None:
        print "No eye cup object passed, reading eye cup info from file."
        eye_cup = Eye_cup.from_eye_cup_file()
    if rf == None:
        print "No RF object passed, reading rf info from file."
        rf = RF.from_rf_file()
    # if we're supposed to save the frames, scale the window first.
    if save_frames_factor != None:
        eye_cup.scale(save_frames_factor)
        rf.scale(save_frames_factor)
        win_size = (win_size[0]*save_frames_factor,
                        win_size[1]*save_frames_factor)
    # set up the display window
    # rgb = 1.0 is white, and sets that as the default background
    if window == None: # if no window object passed, create one.
        myWin = visual.Window( win_size, allowGUI=False, fullscr=False, pos=(0,-900), rgb=1.0*shade,
                                viewScale = (2.0*rf.size[0]/float(win_size[0]),2.0*rf.size[1]/float(win_size[1])), 
                                viewPos  = (2.0*rf.center[0]/win_size[0],2.0*rf.center[1]/win_size[1]), 
                                viewOri  = rf.ori )
    else: # window object was passed, so use it.
        myWin = window

    patches = []
    for mpi in moving_patch_info:
        # construct the patch
        patches.append( visual.PatchStim( myWin, tex=mpi['patch_info']['tex'],
                                   size =mpi['patch_info']['size'], 
                                   units=mpi['patch_info']['units'],
                                   mask =mpi['patch_info']['mask']))

    # prepare the eye_cup for drawing.
    eye_cup.prep_for_drawing(myWin, shade=shade, opacity=0.07, 
                             rf=rf)
    rf.prep_for_drawing(myWin, shade=shade, opacity=0.07, reverse_rf=True)

    # -----------------------
    # --  WAIT FOR A PULSE --
    # -----------------------
    if not preview:
        import parallel
        from pport_functions import wait_for_pulse
        p = parallel.Parallel()     # get access to the parallel port
        print "Waiting for pulse..."
        wait_for_pulse(p) # poll the parallel port for a pulse every pport_dt seconds
    else:
        print "Not Waiting for pulse..."

    # start keeping track of time
    trialClock = core.Clock()       # start the clock
    times = [0.0]                   # time begins at 0.0

    # --------------------------
    # --  SHOW THE STIMULUS   --
    # --------------------------
    break_loop = False
    for i in xrange(num_frames):

        if save_frames_factor == None:
            eye_cup.draw()
            rf.draw()
        # set the positions and orientations of all the patches
        for mpi, patch in zip(moving_patch_info, patches):
            patch.setPos( (mpi['positions'][i][0], 
                           mpi['positions'][i][1]), units='norm')
            patch.setOri( -mpi['orientations'][i] )
            patch.draw()

        if save_frames_factor != None:
            myWin.getMovieFrame(buffer='back')
            visual.GL.glClear(visual.GL.GL_COLOR_BUFFER_BIT | visual.GL.GL_DEPTH_BUFFER_BIT)
            #myWin.flip()
        else:
            # flip the back buffer to the front and the front to the back
            myWin.flip()

        # keep track of when buffers were flipped
        times.append( trialClock.getTime() )
        
        for key in event.getKeys():
            if key in ['escape', 'q']:
                break_loop = True

        if break_loop:
            break

    if window == None: # no window object was passed.
        myWin.close()
 
    if save_frames_factor == None:
        return times
    else:
        return times, myWin.movieFrames

